package edu.calvin.djs32.cs214.interpreters.calculator;

import static org.easymock.EasyMock.expect;
import static org.junit.Assert.assertSame;

import org.easymock.EasyMock;
import org.junit.Before;
import org.junit.Test;

import edu.calvin.cs214.hobbes2008.HobbesTIR;
import edu.calvin.cs214.hobbes2008.IdentifierTIR;
import edu.calvin.cs214.hobbes2008.LambdaTIR;
import edu.calvin.cs214.hobbes2008.TupleTIR;
import edu.calvin.cs214.hobbes2008.environments.IEnvironment;
import edu.calvin.djs32.cs214.interpreters.support.EasyMockControl;


public class ApplicableLambdaTest {
	
	private EasyMockControl myControl;
	private IdentifierTIR myIdentifier;
	private HobbesTIR myBody;
	private LambdaTIR myLambda;
	private ApplicableLambda myApplicableLambda;
	
//	private IEnvironment<HobbesTIR> myVariables;
	
	@SuppressWarnings("unchecked")
	@Before
	public void setUp() {
		myControl = new EasyMockControl();
		myIdentifier = new IdentifierTIR("ident");
		myBody = myControl.createMock(HobbesTIR.class);
		myLambda = new LambdaTIR(myIdentifier, myBody);
		myApplicableLambda = new ApplicableLambda(myLambda);
		
//		myVariables = myControl.createMock(IEnvironment.class);
	
	}
	
	@SuppressWarnings("unchecked")
	@Test
	public void shouldApplyWithEmptyArguments(){
		ICalculator calculator = myControl.createMock(ICalculator.class);
		HobbesTIR arguments = myControl.createMock(HobbesTIR.class);
		IEnvironment<HobbesTIR> variables = myControl.createMock(IEnvironment.class);
		IEnvironment<Applicable> primitives = myControl.createMock(IEnvironment.class);
		IEnvironment<HobbesTIR> nestedEnvironment = myControl.createMock(IEnvironment.class);
		HobbesTIR result = myControl.createMock(HobbesTIR.class);
		
		expect(calculator.getVariables()).andReturn(variables);
		expect(variables.create()).andReturn(nestedEnvironment);
		nestedEnvironment.add(EasyMock.same(myIdentifier), EasyMock.eq(new TupleTIR(arguments)));
		expect(calculator.getPrimitives()).andReturn(primitives);
		Calculator nestedCalculator = new Calculator(primitives, nestedEnvironment);
		expect(myLambda.getBody().accept(nestedCalculator)).andReturn(result);
		
		myControl.replay();
		
		assertSame(result, myApplicableLambda.apply(calculator, arguments));
		
		myControl.verify();
		
	}
	
	@SuppressWarnings("unchecked")
	@Test
	public void shouldApplyWithMultipleArguments(){
		ICalculator calculator = myControl.createMock(ICalculator.class);
		HobbesTIR[] arguments = myControl.createMocks(new HobbesTIR[3], HobbesTIR.class);
		IEnvironment<HobbesTIR> variables = myControl.createMock(IEnvironment.class);
		IEnvironment<Applicable> primitives = myControl.createMock(IEnvironment.class);
		IEnvironment<HobbesTIR> nestedEnvironment = myControl.createMock(IEnvironment.class);
		HobbesTIR result = myControl.createMock(HobbesTIR.class);
		
		expect(calculator.getVariables()).andReturn(variables);
		expect(variables.create()).andReturn(nestedEnvironment);
		nestedEnvironment.add(EasyMock.same(myIdentifier), EasyMock.eq(new TupleTIR(arguments)));
		expect(calculator.getPrimitives()).andReturn(primitives);
		Calculator nestedCalculator = new Calculator(primitives, nestedEnvironment);
		expect(myLambda.getBody().accept(nestedCalculator)).andReturn(result);
		
		myControl.replay();
		
		assertSame(result, myApplicableLambda.apply(calculator, arguments));
		
		myControl.verify();		
	}

}
